接續上一篇的資料庫實作,我們來討論資料庫設計準則,包括【正規化】(Normalization)、反正規化 (Denormalization)、主鍵(Primary key)/索引(Index)/外來鍵(Foreign key)/代理鍵(Surrogate Key)...等。
線上交易系統(Online transaction processing, OLTP)的資料庫通常採用【正規化】(Normalization)設計準則,線上分析處理系統(Online analytical processing, OLAP)的資料庫則採用【星狀結構】(Star schema)設計準則,而資料倉儲(Data warehouse)又是採用另一種設計準則,主要是因應使用目的的不同而有所調整。例如:
一般商業交易系統會採用【正規化】,要確保資料一致性與完整性。
圖一. 【正規化】(Normalization)
多維度分析(Multi-dimensional analysis)會希望所有維度(Dimension)階層(國家/縣市/鄉鎮、年/月/日)集中在一起,以利多個維度展現,故會合併資料表,形成【星狀結構】,中央為儲存衡量指標的事實表(Fact table)。
圖二. 【星狀結構】(Star schema)
上一篇討論的投票系統屬於OLTP,因此會遵循【正規化】(Normalization)設計,根據學理研究,正規化分為很多等級,可參閱【Normal Forms -- Simple Talk】一文:
嚇死人了,怎記的了那麼多,還好,實務上只要遵守前3個正規化即可。
question_text, choice_1, choice_2, choice_3, choice_4, choice_5
以上假設一個問題最多只有5個答案選項,這種方式查詢很方便,但是有一天需求變更,變成5個以上的答案選項,資料表就要重整了。
假設有一個表格如下:
圖三. 每日股價
這是從交易所下載的股價,主鍵為【股票代碼】+【日期】,因為每支股票一天只有一筆資料,開/高/低/收依賴主鍵沒有問題,但【簡稱】則只依賴【股票代碼】,故違反2NF,會有以下副作用:
股票基本資料表:股票代碼、簡稱。
股票交易表:股票代碼、日期、開/高/低/收
依照正規化會將資料拆分成許多表,可以保證資料的一致性與完整性,但是有時候還是抵擋不了誘惑,會進行反正規化,加入冗餘(Redundent, 或稱重複)及計算欄位,以利資料查詢/報表製作的方便性及效率提升,例如
這些都違反正規化,但是,為了效率不擇手段,真的需要這樣作時,要如何降低副作用呢? 筆者建議以下方法:
正規化將資料拆分的很零散,查詢時必須連結(join)許多資料表,效率就會不彰,因此,必須建立【索引】(Index)加快查詢速度,資料庫會內建資料結構以利搜尋,使用索引的效能會加快數倍,主鍵(Primary key)/外來鍵(Foreign key)都特殊的索引,主鍵前面已介紹過,外來鍵是子資料表(Child table)的特定欄位是另一資料表(父資料表)的主鍵,它對於ORM非常重要,可以引導ORM套件如何生成SQL,同時,對於資料庫執行計劃(Execution plan)也是重要的參考因素。
另外,ORM通常會採用【代理鍵】(Surrogate Key),以自動給號(Auto generation)的欄位作為主鍵,取代【自然鍵】(Natural Key)或稱【商業鍵】(Business Key),如公司代碼、商品編號...等,優點如下:
相關討論可參閱【What are the benefits and drawbacks of using surrogate keys in a data warehouse?】及【Surrogate Key in DBMS】。
上篇的模型(13\django\mysite\polls\models.py)如下:
# 問題
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
# 答案選項
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
Django預設會採用代理鍵(Surrogate Key),如下圖的id。
自動生成的Create table SQL如下:
CREATE TABLE "polls_question" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"question_text" varchar(200) NOT NULL,
"pub_date" datetime NOT NULL
);
正規化是關聯式資料庫設計的準則,缺點是資料會拆分的很零散,因此有NoSQL資料庫的誕生,它分為多種類型:
NoSQL資料庫優點是可以很迅速的比對及搜尋到單一筆資料,但要依條件篩選多筆資料,效率就較差,為改善此缺點,可以設計索引或採取分散式儲存(Sharding),NoSQL資料庫也需要良好的資料庫設計準則,主流的資料庫有MongoDB、Neo4j...等,設計準則可參照他們各自的文件。
資料庫設計是開發應用程式必備的技能,另外,交易(Transaction)控制的機制也是撰寫程式要特別注意的事項,遇到效能不彰時,系統要如何調校(Tuning),也都是程式設計師能力提升的重點。
下一篇我們討論網頁開發架構,並實作一個完整的網頁應用程式,包括路由(Routing)、視圖(View)...等尚未討論的部份。